using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using System.Runtime.Remoting;
using System.Windows;
using System.Xml;
using System.Resources;
using System.Security.Policy;
using ViewOn.AddInsHostContract;
using ViewOnCore;
using ViewOnCore.AddIn;
using ViewOnCore.Helper.Debug;
using ViewOnCore.HostContract;
using ViewOnCore.Device;
using ViewOnCore.Visual;

namespace ViewOn.AddIns
{
    public delegate void AddInEvent(object addIn);

    // http://vincentlaine.developpez.com/tuto/dotnet/appdomain/csharp/
    static public class AddInStore
    {

        #region Variables

        //---- Inner variables
        static private AppDomain _addInsDomain;
        //static private string _addInsDirectory = System.Windows.Forms.Application.StartupPath + "\\AddIns";

        //---- Add Ins
        static private List<object> _addIns = new List<object>();

        //---- Events
        static public event AddInEvent AddInRegistered;
        static public event AddInEvent AddInUnregistered;

        #endregion

        #region Rebuild

        static public void Rebuild(string path)
        {
            //---- Create the AppDomain
            //AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
            //setup.ShadowCopyFiles = "false";
            //setup.ShadowCopyDirectories = "false";
            //setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
            //setup.DisallowBindingRedirects = true;
            ////setup.PrivateBinPathProbe = "*";

            //_addInsDomain = AppDomain.CreateDomain("AddInsDomain", null, setup);
            _addInsDomain = AppDomain.CurrentDomain;
            _addInsDomain.SetupInformation.ShadowCopyFiles = "false";
            _addInsDomain.SetupInformation.ShadowCopyDirectories = "false";

            foreach (string folderPath in Directory.GetDirectories(path))
                LoadAddIn(folderPath);
        }

        static private void LoadAddIn(string path)
        {
            foreach (string filePath in Directory.GetFiles(path, "*.dll"))
            {
                if (filePath.EndsWith("ViewOnCore.dll") ||
                    filePath.EndsWith("Interop.DexterLib.dll") ||
                    filePath.EndsWith("starburnx.dll") ||
                    filePath.EndsWith("taglib-sharp.dll") ||
                    filePath.EndsWith("taglib-sharp.dll") ||
                    filePath.EndsWith("bass.dll") ||
                    filePath.EndsWith("Bass.Net.dll") ||
                    filePath.EndsWith("bass_aac.dll") ||
                    filePath.EndsWith("bass_fx.dll") ||
                    filePath.EndsWith("basscd.dll") ||
                    filePath.EndsWith("bassenc.dll") ||
                    filePath.EndsWith("bassmix.dll") ||
                    filePath.EndsWith("basswma.dll"))
                    continue;

                try
                {
                    //---- Load the add ins
                    Assembly assembly = Assembly.LoadFrom(filePath);//_addInsDomain.Load(filePath);
                    if (assembly != null)
                    {
                        List<Type> types = GetTypesWithAttribute(assembly, typeof(AddInAttribute));
                        foreach (Type addInType in types)
                            if (addInType.IsClass && !addInType.IsAbstract)
                            {
                                //---- Register the add in
                                RegisterAddIn(assembly, addInType);
                            }
                        JITAssembly(assembly);
                        LoadAssemblyResources(assembly);
                    }
                }
                catch (Exception exception)
                {
                    LogManager.Handle(exception);
                }
            }
        }

        static private List<Type> GetTypesWithAttribute(Assembly assembly, Type customAttribute)
        {
            List<Type> list = new List<Type>();
            foreach (Module module in assembly.GetModules())
                foreach (Type type in module.GetTypes())
                    if (type.GetCustomAttributes(customAttribute, true).Length > 0)
                        list.Add(type);

            return list;
        }

        #endregion

        #region LoadAssemblyResources

        static private void LoadAssemblyResources(Assembly assembly)
        {
            Stream stream = assembly.GetManifestResourceStream(assembly.GetName().Name + ".g.resources");
            if (stream == null)
                return;

            using (ResourceReader reader = new ResourceReader(stream))
            {
                foreach (DictionaryEntry entry in reader)
                {
                    string resourceName = ((string)entry.Key).Replace(".baml", ".xaml");
                    string packUri = String.Format(@"/{0};component/{1}", assembly.GetName().Name, resourceName);
                    Uri uri = new Uri(packUri, UriKind.Relative);
                    string typeName = resourceName.Replace("/", ".").Replace(".xaml", "");
                    try
                    {
                        Type type = assembly.GetType(typeName);
                        if (type == null)
                            continue;
                        object control = Activator.CreateInstance(type);
                        if (control == null)
                            continue;

                        Application.LoadComponent(control, uri);
                        ResourceDictionary dictionary = Application.LoadComponent(uri) as ResourceDictionary;
                        if (dictionary != null && !Application.Current.Resources.MergedDictionaries.Contains(dictionary))
                            Application.Current.Resources.MergedDictionaries.Add(dictionary);
                    }
                    catch
                    {
                        System.Console.WriteLine("Resource not found : " + uri.ToString());
                    }
                }
            }
        }

        #endregion

        #region Register / Unregister

        static public void RegisterAddIn(Assembly assembly, Type type)
        {
            try
            {
                object addInInstance = assembly.CreateInstance(type.FullName);

                _addIns.Add(addInInstance);

                //---- Notify everyone
                if (AddInRegistered != null)
                    AddInRegistered(addInInstance);
            }
            catch (Exception exception)
            {
                LogManager.Handle(exception);
            }
        }

        /// <summary>
        /// Unregister the Add in.
        /// </summary>
        /// <param name="addInHandle">The Add in's handle</param>
        static public void UnregisterAddIn(string addInHandle)
        {
            //foreach (object addIn in _addIns)
            //    if (addIn.AddInHandle == addInHandle)
            //    {
            //        UnregisterAddIn(addIn);
            //        return;
            //    }
        }

        static public void UnregisterAddIn(object addInInstance)
        {

            _addIns.Remove(addInInstance);
            //((MediaDeviceAddIn)addInInstance).OnSaveChanges();

            //---- Notify the add in
            //addInInstance.OnUnregistered();

            //---- Notify everyone
            if (AddInUnregistered != null)
                AddInUnregistered(addInInstance);
        }

        #endregion

        #region JITAssembly

        //    //---- Pre load the assemblies
        //    Assembly assembly = Assembly.GetAssembly(typeof(UFOzApp));
        //    JITAssembly(assembly);

        static private Dictionary<string, string> _jittedAssemblies = new Dictionary<string, string>();
        static private void JITAssembly(Assembly assembly)
        {
            if (_jittedAssemblies.ContainsKey(assembly.FullName))
                return;
            _jittedAssemblies.Add(assembly.FullName, assembly.FullName);

            assembly.GetTypes();
            foreach (Module module in assembly.GetModules())
            {
                System.Runtime.CompilerServices.RuntimeHelpers.RunModuleConstructor(module.ModuleHandle);
                foreach (Type type in module.GetTypes())
                    try
                    {
                        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle);
                    }
                    catch { }
            }

            foreach (Assembly subAssembly in AppDomain.CurrentDomain.GetAssemblies())
                JITAssembly(subAssembly);
        }

        #endregion

        #region ApplicationExit

        static public void ApplicationExit()
        {
            for (int index = _addIns.Count - 1; index > -1; index--)
                UnregisterAddIn(_addIns[index]);
        }

        #endregion

    }

}